/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// 1. Promise 就是一个类, 在执行这个类的时候需要传递一个执行器, 执行器会立即执行
// 2. Promise 有三个状态(一旦状态确定就不可更改)
//   - pending
//   - fulfilled
//   - rejected
// 3. resolve 和 reject 函数就是用来修改状态的
//   resolve: 将状态变成 fulfilled
//   reject: 将状态变成 rejected
// 4. then 方法的作用就是判断状态并执行相应的回调函数(then 方法是被定义在原型对象中的):
//     如果 状态成功 -> 调用成功的回调函数
//     如果 状态失败 -> 调用失败的回调函数
// 5. then 成功回调有一个参数: 表示成功后的值
// 6. then 失败回调有一个参数: 表示失败后的原因


const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {

  // 构造函数
  constructor(exector) {
    // 捕获执行器执行的错误
    try {
      // 执行器, 立即执行
      exector(this.resolve, this.reject);
    } catch (error) {
      this.reject(error)
    }
  }


  status = PENDING;  // Promise 状态
  value = undefined;   // 成功之后的值
  reason = undefined;   // 失败之后的原因

  successCallback = []; // 成功回调函数, 使用数组才可以存储多个回调函数
  failCallback = []; // 失败回调函数, 使用数组才可以存储多个回调函数

  // 使用箭头函数, 让 this 指向当前这个类的实例对象, 防止后期调用的时候 this 指向为 window 或 undefined
  // 将状态更改为成功
  resolve = value => {
    // 如果状态不是 PENDING 状态, 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态改为成功
    this.status = FULFILLED;
    this.value = value;
    // 遍历 `成功回调函数` 列表, 依次取出执行
    while (this.successCallback.length) this.successCallback.shift()();
  }

  // 将转年糕条更改为失败
  reject = reason => {
    // 如果状态不是 PENDING 状态, 防止状态修改, 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态改为失败
    this.status = REJECTED;
    this.reason = reason;
    // 遍历 `失败回调函数` 列表, 依次取出执行
    while (this.failCallback.length) this.failCallback.shift()();
  }

  // 成功回调: successCallback
  // 失败回调: failCallback
  then (successCallback, failCallback) {
    // 处理 then 方法内容为空值的情况(当 then 方法内容为空时, 将 value 传递给下一个 then 方法).
    successCallback = successCallback ? successCallback : value => value;
    failCallback = failCallback ? failCallback : reason => { throw reason };
    let promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          // 捕获 then 方法中回调函数的错误
          try {
            // 成功 -> 调用该回调函数, 并将成功之后的值传递给成功回调函数, 然后再次将成功回调函数的执行结果传递给下一个 then 方法的回调函数
            let x = successCallback(this.value);
            // 判断 x 是 普通值还是 promise 对象:
            //   普通值 -> 调用 resolve
            //   promise -> 查看 promise 返回的结果 -> 根据返回的结果 决定调用 resolve 还是 reject

            // 判断 x 和 promise2 是否相等, 如果相等则表示发生了循环调用.
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        // 失败 -> 调用该回调函数, 并将失败之后的原因传递给失败回调函数, 然后再次将失败回调函数的执行结果传递给下一个 then 方法的失败回调函数

        setTimeout(() => {
          // 捕获 then 方法中回调函数的错误
          try {
            // 成功 -> 调用该回调函数, 并将成功之后的值传递给成功回调函数, 然后再次将成功回调函数的执行结果传递给下一个 then 方法的回调函数
            let x = failCallback(this.reason);
            // 判断 x 是 普通值还是 promise 对象:
            //   普通值 -> 调用 resolve
            //   promise -> 查看 promise 返回的结果 -> 根据返回的结果 决定调用 resolve 还是 reject

            // 判断 x 和 promise2 是否相等, 如果相等则表示发生了循环调用.
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            // 捕获 then 方法中回调函数的错误
            try {
              // 成功 -> 调用该回调函数, 并将成功之后的值传递给成功回调函数, 然后再次将成功回调函数的执行结果传递给下一个 then 方法的回调函数
              let x = successCallback(this.value);
              // 判断 x 是 普通值还是 promise 对象:
              //   普通值 -> 调用 resolve
              //   promise -> 查看 promise 返回的结果 -> 根据返回的结果 决定调用 resolve 还是 reject

              // 判断 x 和 promise2 是否相等, 如果相等则表示发生了循环调用.
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error)
            }
          }, 0)
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            // 捕获 then 方法中回调函数的错误
            try {
              // 成功 -> 调用该回调函数, 并将成功之后的值传递给成功回调函数, 然后再次将成功回调函数的执行结果传递给下一个 then 方法的回调函数
              let x = failCallback(this.reason);
              // 判断 x 是 普通值还是 promise 对象:
              //   普通值 -> 调用 resolve
              //   promise -> 查看 promise 返回的结果 -> 根据返回的结果 决定调用 resolve 还是 reject

              // 判断 x 和 promise2 是否相等, 如果相等则表示发生了循环调用.
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error)
            }
          }, 0)
        });
      }
    })
    return promise2
  }

  // 静态方法
  static all (array) {
    let result = [];

    // 这个 index 是为了解决 for 循环中存在的异步操作, 我们需要等待所有的异步操作执行完成才能执行 resolve 方法来结束 all 方法.
    let index = 0;

    // all 方法的最终结果还是个 Promise 对象
    return new MyPromise((resolve, reject) => {
      // 向数组添加值的方法
      function addData (key, value) {
        result[key] = value;
        index++;
        // 当 index 等于 数组长度的时候, 说明 all 中所有项(包含异步操作)都执行完了, 可以执行 resolve 方法来结束 all 方法
        if (index === array.length) {
          resolve(result);
        }
      }

      // 遍历数组
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(value => {
            addData(i, value);
          }, reason => {
            reject(reason);
          })
        } else {
          // 普通值, 直接放入 result 数组中
          addData(i, array[i]);
        }
      }
    })

  }

  // 将给定的值(普通值 或 Promise 对象)转换成 Promise 对象
  static resolve (value) {
    // 判断  value 是不是一个 Promise 对象, 如果是就直接返回
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }

  // 1. 无论当前这个 Promise 对象是成功还是失败,最终都会执行这个 finally 方法.
  // 2. finally 方法之后可以通过 then 方法来获得这个 Promise 对象最终的返回结果.
  finally (callback) {
    // 无论成功还是失败都要调用finally 方法传入的回调函数
    // 同时返回一个 Promise 对象, 并将结果返回给下一个 then 方法中设置的回调函数.
    return this.then(value => {
      // 不管传入的值是什么类型, 都将其转换成 Promise 对象
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason });
    })
  }

  // catch 方法用于处理 Promise 对象 最终状态是失败的时候
  catch (failCallback) {
    // 其实 catch 方法的底层还是 then 方法. 只注册了失败回调
    return this.then(undefined, failCallback)
  }
}

// 判断是否产生了循环调用;
// 判断 回调函数 x 是 `普通值` 还是 `Promise 对象`;
function resolvePromise (promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

module.exports = MyPromise;